www.gusucode.com > VC++ 三维图形生成和察看工具 > VC++ 三维图形生成和察看工具/code/mesh/Lib3D/Mesh3d.cpp

    //Download by http://www.NewXing.com
//********************************************
// Mesh3d.cpp
//********************************************
// class CMesh3d
//********************************************
// alliez@usc.edu
// Created : 15/01/98
// Modified : 15/01/98
//********************************************

#include "stdafx.h"
#include <math.h>
#include "Base3d.h"
#include "Mesh3d.h"
#include "ColorRamp.h"
#include "AVLInd.h" // Gaspard Breton's stuff
#include "WmfTools.h"

//////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////

//********************************************
// Constructor
//********************************************
CMesh3d::CMesh3d()
{
	m_ListDone = 0;
	m_Modified = 1;
	m_NormalBinding = NORMAL_PER_FACE;
	m_ColorBinding = COLOR_PER_VERTEX;
	m_Name = _T("Mesh");
	// Texture
	m_IndexTexture = -1;
	m_pTextureCoordinate = NULL;
	m_pTextureCoordinateIndex = NULL;
	m_Show = 1;
}

//********************************************
// Destructor
//********************************************
CMesh3d::~CMesh3d()
{
	Free();
}

//********************************************
// Free
//********************************************
void CMesh3d::Free()
{
	//TRACE("Cleanup mesh %x\n",this);
	m_ArrayVertex.Free();
	m_ArrayFace.Free();
	if(m_pTextureCoordinate != NULL)
		delete [] m_pTextureCoordinate;
	if(m_pTextureCoordinateIndex != NULL)
		delete [] m_pTextureCoordinateIndex;
}


//////////////////////////////////////////////
// OPENGL
//////////////////////////////////////////////

//********************************************
// BuildList
//********************************************
int CMesh3d::glBuildList()
{
	//TRACE(" Start building list ...\n");

	// Check for valid mesh
	if(m_ArrayVertex.GetSize() == 0 ||
		 m_ArrayFace.GetSize() == 0)
	{
		TRACE("CMesh3d::BuildList : empty mesh\n");
		return 0;
	}

  if(!m_Modified && m_ListDone)
		return 0;

	// Erase last list
	::glDeleteLists(m_ListOpenGL,1);

	// Search for a new list
	m_ListOpenGL = ::glGenLists(1);
	if(m_ListOpenGL == 0)
	{
		TRACE("CMesh3d::BuildList : unable to build DrawList\n");
		return 0;
	}

	// Start list
	::glNewList(m_ListOpenGL,GL_COMPILE_AND_EXECUTE);
		glDrawDirect();
	::glEndList();

	// List is done now
	m_ListDone = 1;
	m_Modified = 0;

	return 1;
}

//*********************************
// glDrawDirect 
//*********************************
int CMesh3d::glDrawDirect()
{
	unsigned int NbVertex = (unsigned int)m_ArrayVertex.GetSize();
	unsigned int NbFace = (unsigned int)m_ArrayFace.GetSize();

	if(!NbVertex)
		return 0;
	if(!NbFace)
		return 0;

	CFace3d *pFace;
	CVector3d *pVector;
	CColor *pColorPrevious;

	::glPushMatrix();

	// Position / translation / scaling
	glTranslatef(m_Transform.GetTranslation()->x(),
			         m_Transform.GetTranslation()->y(),
							 m_Transform.GetTranslation()->z());

	glScalef(m_Transform.GetScale()->x(),
			     m_Transform.GetScale()->y(),
			     m_Transform.GetScale()->z());

	glRotatef(m_Transform.GetValueRotation(),
			      m_Transform.GetRotation()->x(),
			      m_Transform.GetRotation()->y(),
			      m_Transform.GetRotation()->z());

	// Init color
	pFace = m_ArrayFace[0];
	pColorPrevious = pFace->GetColor();
	::glColor3ub(pFace->GetColor()->r(),pFace->GetColor()->g(),pFace->GetColor()->b());

	// Triangles
	::glBegin(GL_TRIANGLES);
	for(unsigned int i=0;i<NbFace;i++)
	{
		{
			pFace = m_ArrayFace[i];
			ASSERT(pFace != NULL);

			// Normal (per face)
			if(m_NormalBinding == NORMAL_PER_FACE)
			{
				pVector = pFace->GetNormal();
				::glNormal3f(pVector->x(),pVector->y(),pVector->z());
			}

			// Color (per face)
			if(m_ColorBinding == COLOR_PER_FACE && 
				 pColorPrevious != pFace->GetColor())
			{
				::glColor3ub(pFace->GetColor()->r(),pFace->GetColor()->g(),pFace->GetColor()->b());
				pColorPrevious = pFace->GetColor();
			}

			for(int j=0;j<3;j++)
			{
				// Normal
				if(m_NormalBinding == NORMAL_PER_VERTEX)
				{
					pVector = pFace->v(j)->GetNormal();
					::glNormal3f(pVector->x(),pVector->y(),pVector->z());
				}

				// Color (per vertex)
				if(m_ColorBinding == COLOR_PER_VERTEX && 
					 pColorPrevious != pFace->v(j)->GetColor())
				{
					::glColor3ub(pFace->v(j)->GetColor()->r(),pFace->v(j)->GetColor()->g(),pFace->v(j)->GetColor()->b());
					pColorPrevious = pFace->v(j)->GetColor();
				}

				// Texture coordinate (if needed)
				if(m_IndexTexture != -1)
				{
					glTexCoord2f(m_pTextureCoordinate[2*m_pTextureCoordinateIndex[3*i+j]],
						           m_pTextureCoordinate[2*m_pTextureCoordinateIndex[3*i+j]+1]);	
				}

				// Vertex
				::glVertex3f(pFace->v(j)->x(),pFace->v(j)->y(),pFace->v(j)->z());
			}
		}
	}

	::glEnd();
	::glPopMatrix();
	return 1;
}

//********************************************
// Draw
//********************************************
int CMesh3d::glDraw()
{
	if(!m_Show)
		return 0;

	// Build list at first
	if(!m_ListDone || m_Modified)
		glBuildList();

	// Search for a new list
	if(::glIsList(m_ListOpenGL)==GL_TRUE)
	{
	  ::glCallList(m_ListOpenGL);
		return 1;
	}
	else
	{
		TRACE(" CMesh3d::Draw : unable to draw list %d\n",m_ListOpenGL);
		return 0;
	}
}





//////////////////////////////////////////////
// DATAS
//////////////////////////////////////////////

//********************************************
// Copy
//********************************************
void CMesh3d::Copy(CMesh3d *pMesh)
{
	// Vertices
	int NbVertex = pMesh->NbVertex();
	m_ArrayVertex.SetSize(NbVertex);
	for(int i=0;i<NbVertex;i++)
		m_ArrayVertex.SetAt(i,new CVertex3d(pMesh->GetVertex(i)));

	// Faces
	int NbFace = pMesh->NbFace();
	m_ArrayFace.SetSize(NbFace);
	for(i=0;i<NbFace;i++)
	{
		CFace3d *pFace = pMesh->GetFace(i);
		m_ArrayFace.SetAt(i,new CFace3d(
			m_ArrayVertex[pMesh->IndexFrom(pFace->v1())],
			m_ArrayVertex[pMesh->IndexFrom(pFace->v2())],
			m_ArrayVertex[pMesh->IndexFrom(pFace->v3())]));
	}

	// Transform
	m_Transform.Copy(pMesh->GetTransform());
}


//********************************************
// GetType
//********************************************
int CMesh3d::GetType()
{
	return TYPE_MESH3D;
}

//********************************************
// IsValid
//********************************************
int CMesh3d::IsValid()
{
	int NbFace = m_ArrayFace.GetSize();
	for(int i=0;i<NbFace;i++)
		if(!m_ArrayFace[i]->IsValid())
			return 0;
	return 1;
}


//********************************************
// DeleteVertex
//********************************************
int CMesh3d::DeleteVertex(CVertex3d *pVertex)
{
	int size = m_ArrayVertex.GetSize();
	for(int i=0;i<size;i++)
	{
		CVertex3d *pV = m_ArrayVertex[i];
		if(pV == pVertex)
		{
			m_ArrayVertex.RemoveAt(i);
			delete pVertex;
		  return 1;
		}
	}
	return 0;
}

//********************************************
// DeleteVertex
//********************************************
int CMesh3d::DeleteVertex(int index)
{
	if(index < m_ArrayVertex.GetSize())
	{
		CVertex3d *pVertex = (CVertex3d *)m_ArrayVertex[index];
		m_ArrayVertex.RemoveAt(index);
		delete pVertex;
		return 1;
	}
	return 0;
}

//********************************************
// DeleteFace
//********************************************
int CMesh3d::DeleteFaceNbNeighbors(int NbNeighbor)
{
	TRACE("Delete faces which has %d neighbors...",NbNeighbor);
	int deleted = 0;
	for(int i=0;i<m_ArrayFace.GetSize();i++)
	{
		CFace3d *pFace = m_ArrayFace[i];
		if(pFace->NbFaceNeighbor() == NbNeighbor)
		{
			m_ArrayFace.RemoveAt(i);
			delete pFace;
			i--;
			deleted++;
		}
	}
	TRACE("%d face(s) deleted\n",deleted);
	return deleted;
}


//////////////////////////////////////////////
// RANGE
//////////////////////////////////////////////

//********************************************
// Range
//********************************************
void CMesh3d::Range(int coord, 
										float *min,
										float *max)
{
	ASSERT(coord >= 0 && coord <= 2);
	int NbVertex = m_ArrayVertex.GetSize();
	float Min = m_ArrayVertex[0]->Get(coord);
	float Max = Min;
	for(int i=1;i<NbVertex;i++)
	{
		float value = m_ArrayVertex[i]->Get(coord);
		if(value < Min)
			Min = value;
		if(value > Max)
			Max = value;
	}
	*min = Min;
	*max = Max;
}

//********************************************
// Range (apply)
//********************************************
void CMesh3d::Range(int coord, 
										float min,
										float max)
{
	TRACE("Range mesh...");
	float Min,Max;
	Range(coord,&Min,&Max);
	TRACE("old : (%g,%g) -> (%g %g)",Min,Max,min,max);
	Offset(coord,-Min);
	Scale(coord,(max-min)/(Max-Min));
	Offset(coord,min);
	TRACE("ok\n");
}

//********************************************
// Scale
//********************************************
void CMesh3d::Scale(int coord,
										float scale)
{
	int NbVertex = m_ArrayVertex.GetSize();
	for(int i=0;i<NbVertex;i++)
		m_ArrayVertex[i]->Set(coord,m_ArrayVertex[i]->Get(coord) * scale);
	m_Modified = 1;
}

//********************************************
// Offset
//********************************************
void CMesh3d::Offset(int coord,
										 float offset)
{
	int NbVertex = m_ArrayVertex.GetSize();
	for(int i=0;i<NbVertex;i++)
		m_ArrayVertex[i]->Set(coord,m_ArrayVertex[i]->Get(coord) + offset);
	m_Modified = 1;
}


//////////////////////////////////////////////
// PROCESSING
//////////////////////////////////////////////

//********************************************
// BuildAdjacency
// From VertexArray and FaceArray, build
// neighboring vertices and faces, using
// edge and vertex sharing
//********************************************
int CMesh3d::BuildAdjacency()
{
	// Check for valid sizes
	int NbVertex = m_ArrayVertex.GetSize();
	int NbFace = m_ArrayFace.GetSize();
	TRACE("Build adjacency (%d faces, %d vertices)",NbFace,NbVertex);
	if(NbVertex < 3 || NbFace <= 0)
	{
		TRACE("  invalid array sizes\n");
		return 0;
	}

	// At first, clear all neighbors for each face
	//TRACE("  clear face neighbors\n");
	TRACE(".");
	for(int i=0;i<NbFace;i++)
		for(int k=0;k<6;k++)
			m_ArrayFace[i]->f(k,NULL);

	// At first, clear all neighbors for each vertex
	//TRACE("  clear vertex neighbors\n");
	TRACE(".");
	for(i=0;i<NbVertex;i++)
	{
		m_ArrayVertex[i]->RemoveAllFaceNeighbor();
		m_ArrayVertex[i]->RemoveAllVertexNeighbor();
	}

	//*********************************************
	// For each face, set face neighboring vertices 
	//*********************************************
	//TRACE("  set face neighboring vertices\n");
	TRACE(".");
	for(i=0;i<NbFace;i++)
	{
		CFace3d *pFaceCurrent = m_ArrayFace[i];
		for(int j=0;j<3;j++)
			pFaceCurrent->v(j)->AddNeighbor(pFaceCurrent);
	}

	//*********************************************
	// For each vertex, set vertex neighboring,
  // just look on neighboring faces 
	//*********************************************
	//TRACE("  set vertices neighboring vertices\n");
	TRACE(".");
	for(i=0;i<NbVertex;i++)
	{
		CVertex3d *pVertexCurrent = m_ArrayVertex[i];
		int NbFaceNeighbor = pVertexCurrent->NbFaceNeighbor();
		for(int j=0;j<NbFaceNeighbor;j++)
		{
			CFace3d *pFace = pVertexCurrent->GetFaceNeighbor(j);
			for(int k=0;k<3;k++)
				pVertexCurrent->AddNeighbor(pFace->v(k));
		}
	}

	//*********************************************
	// For each face, set face neighboring,
  // just look on faces neighboring vertices
	//*********************************************
	//TRACE("  set faces neighboring faces\n");
	TRACE(".");
	for(i=0;i<NbFace;i++)
	{
		CFace3d *pFaceCurrent = m_ArrayFace[i];
		// For each edge
		for(int j=0;j<3;j++)
		{
			CVertex3d *pVertex = pFaceCurrent->v(j);
			CVertex3d *pNextVertex = pFaceCurrent->v((j+1)%3);
			int NbFaceNeighbor = pVertex->NbFaceNeighbor();
			for(int k=0;k<NbFaceNeighbor;k++)
			{
				// This face contain pVertex
				CFace3d *pFace = pVertex->GetFaceNeighbor(k);
				if(pFace != pFaceCurrent)
				  if(pFaceCurrent->f(j) == NULL)
						if(pFace->HasVertex(pVertex))
							if(pFace->HasVertex(pNextVertex))
								pFaceCurrent->f(j,pFace);
			}
		}
	}

	/*
	// Check
	for(i=0;i<NbFace;i++)
	{
		ASSERT(m_ArrayFace[i]->IsValid());
	}
	*/


	TRACE("ok\n");

	return TRUE;
}

//********************************************
// Rebuild
//********************************************
void CMesh3d::Rebuild()
{
	BuildAdjacency();
	CalculateNormalPerFace();
	CalculateNormalPerVertex();
	SetModified(1);
}

/////////////////////////////////////////////
// DEBUG
//////////////////////////////////////////////

//********************************************
// Trace
//********************************************
void CMesh3d::Trace()
{
	int NbVertex = m_ArrayVertex.GetSize();
	int NbFace = m_ArrayFace.GetSize();

	TRACE("\n");
	TRACE(" Mesh \n");
	TRACE("Vertex : %d\n",NbVertex);
	TRACE("Face   : %d\n",NbFace);

	for(int i=0;i<NbVertex;i++)
		((CVertex3d *)m_ArrayVertex[i])->Trace();

	for(int j=0;j<NbFace;j++)
		((CFace3d *)m_ArrayFace[j])->Trace();
}





//********************************************
// IndexFrom
// Return -1 if failed
//********************************************
int CMesh3d::IndexFrom(CFace3d *pFace)
{
	ASSERT(pFace != NULL);
	int NbFace = m_ArrayFace.GetSize();
	for(int i=0;i<NbFace;i++)
		if(m_ArrayFace[i] == pFace)
			return i;
	return -1;
}

//********************************************
// IndexFrom
// Return -1 if failed
//********************************************
int CMesh3d::IndexFrom(CVertex3d *pVertex)
{
	int NbVertex = m_ArrayVertex.GetSize();
	for(int i=0;i<NbVertex;i++)
		if(m_ArrayVertex[i] == pVertex)
			return i;
	return -1;
}

//********************************************
// Move
//********************************************
void CMesh3d::Move(float dx,float dy,float dz)
{
	int NbVertex = m_ArrayVertex.GetSize();
	for(int i=0;i<NbVertex;i++)
		m_ArrayVertex[i]->Move(dx,dy,dz);
	m_Modified = 1;
}


//********************************************
// FindVertexInFaces
//********************************************
int CMesh3d::FindVertex(CVertex3d *pVertex)
{
	int find = 0;

	int NbFace = m_ArrayFace.GetSize();
	for(int j=0;j<NbFace;j++)
		if(m_ArrayFace[j]->HasVertex(pVertex))
		{
			find = 1;
			TRACE("Find vertex [%x] in face %d\n",pVertex,j);
		}

	int NbVertex = m_ArrayVertex.GetSize();
	for(j=0;j<NbVertex;j++)
		if(m_ArrayVertex[j] == pVertex)
		{
			find = 1;
			TRACE("Find vertex [%x] at position %d\n",pVertex,j);
		}

	for(j=0;j<NbVertex;j++)
	{
		CVertex3d *pV = m_ArrayVertex[j];
		if(pV->HasNeighbor(pVertex))
		{
			find = 1;
			TRACE("Find vertex [%x] in neighbors of vertex %d\n",pVertex,j);
		}
	}

	return find;
}

//********************************************
// FindFace
//********************************************
int CMesh3d::FindFace(CFace3d *pFace)
{
	int find = 0;

	int NbFace = m_ArrayFace.GetSize();
	for(int j=0;j<NbFace;j++)
	{
		CFace3d *pF = m_ArrayFace[j];
		if(pF == pFace)
		{
			find = 1;
			TRACE("Find face [%x] in mesh (index : %d)\n",pFace,j);
		}

		for(int i=0;i<3;i++)
		if(pF->f(i) == pFace)
		{
			find = 1;
			TRACE("Find face [%x] in neighbors %d of face %d\n",pFace,i,j);
		}
	}

	return find;
}



//////////////////////////////////////////////
// NORMALS
//////////////////////////////////////////////


//********************************************
// CalculateNormalPerVertex
//********************************************
int CMesh3d::CalculateNormalPerVertex(void)
{

	int NbVertex = m_ArrayVertex.GetSize();
	int NbFace = m_ArrayFace.GetSize();
	TRACE("Calculate normal per vertex (%d faces, %d vertices)...",NbFace,NbVertex);
	for(int i=0;i<NbVertex;i++)
	{
		CVertex3d *pVertex = m_ArrayVertex[i];
		int NbNormal = 0;
		CVector3d vector;
		int NbFaceNeighbor = pVertex->NbFaceNeighbor();
		for(int j=0;j<NbFaceNeighbor;j++)
		{
			CFace3d *pFace = pVertex->GetFaceNeighbor(j);
			NbNormal += 1;
			vector += pFace->GetNormal();
		}
		if(NbNormal>=1)
		{
			vector.NormalizeL2();
			pVertex->SetNormal(vector);
		}
	}
	m_Modified = 1;
	TRACE("ok\n");

	return 1;
}

//********************************************
// CalculateNormalPerFace
//********************************************
int CMesh3d::CalculateNormalPerFace(void)
{
	int size = m_ArrayFace.GetSize();
	TRACE("Calculate normal per face (%d faces)...",size);
	for(int i=0;i<size;i++)
		{
			CFace3d *pFace = m_ArrayFace[i];
			if(pFace->IsValid())
				pFace->CalculateNormal();
		}
	m_Modified = 1;
	TRACE("ok\n");
	return 1;
}




//********************************************
// ColorSharpEdges
//********************************************
int CMesh3d::ColorSharpEdge(float threshold,
														CColor &color)
{
	int NbFace = m_ArrayFace.GetSize();

	TRACE(" Start ColorSharpEdges\n");
	TRACE("   Faces : %d\n",NbFace);
	for(int i=0;i<NbFace;i++)
		m_ArrayFace[i]->ColorSharpEdge(threshold,color);
	TRACE(" End ColorSharpEdges\n");

	return 1;
}


//********************************************
// SetNormalBinding
//********************************************
void CMesh3d::SetNormalBinding(int type)
{
	m_NormalBinding = type;
	m_Modified = 1;
}

//********************************************
// GetNormalBinding
//********************************************
int CMesh3d::GetNormalBinding(void)
{
	return m_NormalBinding;
}

//********************************************
// SetColorBinding
//********************************************
void CMesh3d::SetColorBinding(int type)
{
	m_ColorBinding = type;
	m_Modified = 1;
}

//********************************************
// SetColor
//********************************************
void CMesh3d::SetColor(CColor &color)
{
	int size = m_ArrayFace.GetSize();
	for(int i=0;i<size;i++)
		m_ArrayFace[i]->SetColor(color);
	m_Modified = 1;
}

//********************************************
// SetColor
//********************************************
void CMesh3d::SetColor(unsigned char r,
											 unsigned char g,
											 unsigned char b)
{
	for(int i=0;i<m_ArrayFace.GetSize();i++)
		m_ArrayFace[i]->SetColor(r,g,b);
	for(i=0;i<m_ArrayVertex.GetSize();i++)
		m_ArrayVertex[i]->SetColor(r,g,b);
	m_Modified = 1;
}

//********************************************
// GetColorBinding
//********************************************
int CMesh3d::GetColorBinding(void)
{
	return m_ColorBinding;
}

//********************************************
// SetFlagOnFaces
//********************************************
void CMesh3d::SetFlagOnFaces(int flag)
{
	int size = m_ArrayFace.GetSize();
	for(int i=0;i<size;i++)
		m_ArrayFace[i]->SetFlag(flag);
}

//********************************************
// GetMaxFlagOnFaces
//********************************************
int CMesh3d::GetMaxFlagOnFaces()
{
	int size = m_ArrayFace.GetSize();
	int max = 0;
	for(int i=0;i<size;i++)
	{
		int tmp = m_ArrayFace[i]->GetFlag();
		max = (tmp > max) ? tmp : max;
	}
	return max;
}

//********************************************
// SetFlagOnVertices
//********************************************
void CMesh3d::SetFlagOnVertices(int flag)
{
	int size = m_ArrayVertex.GetSize();
	for(int i=0;i<size;i++)
		m_ArrayVertex[i]->SetFlag((char)flag);
}


//********************************************
// GetFirstVertexWithFlag
//********************************************
float CMesh3d::GetMeanLengthEdge()
{
	int size = m_ArrayVertex.GetSize();
	double sum = 0;
	for(int i=0;i<size;i++)
		sum += m_ArrayVertex[i]->GetMeanLengthEdgeAround();
	if(size)
		return (float)sum/size;
	else
		return 0.0f;
}





//////////////////////////////////////////////
// SUBDIVISION
//////////////////////////////////////////////

//********************************************
// Alpha
// From Piecewise smooth reconstruction (Hoppe)
//********************************************
float CMesh3d::Alpha(int n)
{
	float tmp = 3.0f + 2.0f * (float)cos(2.0f * 3.14159265359f/(float)n);
	float a = 5.0f/8.0f - (tmp*tmp)/64.0f;
	return (n*(1-a)/a);
}


//********************************************
// SubdivisionLoop
// From Piecewise smooth reconstruction (Hoppe)
//********************************************
// (SIGGRAPH 94) -> Charles Loop 
//********************************************
int CMesh3d::SubdivisionLoop()
{
	// We assume adjacency is built
	int NbVertex = m_ArrayVertex.GetSize();
	SetFlagOnVertices(0); // for boundaries

	// Create subdivision info (edge vertices)
	int NbFace = m_ArrayFace.GetSize();
	TRACE("Start loop's subdivision (%d faces, %d vertices)\n",NbFace,NbVertex);

	TRACE("  subdivision info (%d vertices)\n",m_ArrayVertex.GetSize());

	// For each face
	for(int i=0;i<NbFace;i++)
	{
		CFace3d *pFace = m_ArrayFace[i];

		// Check valid neighboring
		if(!pFace->IsValid())
			continue;

		// On each edge
		for(int IndexEdge=0;IndexEdge<3;IndexEdge++)
		{
			// Get IndexEdge on neighbor
			int IndexCurrent,IndexNeighbor;
			CFace3d *pFaceNeighbor = pFace->f(IndexEdge);

			// No neighbor on this edge, it is a boundary edge
			if(pFaceNeighbor == NULL)
			{
				// Two vertices involved
				CVertex3d *pVertex[2];
				pVertex[0] = pFace->v(IndexEdge);
				pVertex[1] = pFace->v((IndexEdge+1)%3);
				float x = 0.5f*(pVertex[0]->x()+pVertex[1]->x());
				float y = 0.5f*(pVertex[0]->y()+pVertex[1]->y());
				float z = 0.5f*(pVertex[0]->z()+pVertex[1]->z());
				CVertex3d *pNewVertex = new CVertex3d(x,y,z);
				m_ArrayVertex.Add(pNewVertex);
				pFace->v(3+IndexEdge,pNewVertex);
				// Boundary vertices
				pVertex[0]->SetFlag(1);
				pVertex[1]->SetFlag(1);
			}
			else // std case
			{
				VERIFY(pFace->Share2Vertex(pFaceNeighbor,&IndexCurrent,&IndexNeighbor));
				ASSERT(IndexCurrent == IndexEdge);

				// If neighboring face has been treated, then get vertex
				// and go to next step
				if(pFaceNeighbor->GetFlag())
				{
					CVertex3d *pVertex = pFaceNeighbor->v(3+(IndexNeighbor%3));
					pFace->v(3+(IndexEdge%3),pVertex);
					continue;
				}

				// Vertex weighting
				// 0 & 1 : weight : 3, 2 & 3 : weight : 1
				CVertex3d *pVertex[4];

				// Weight : 3
				pVertex[0] = pFace->v(IndexEdge);
				pVertex[1] = pFace->v((IndexEdge+1)%3);
				// Weight : 1
				pVertex[2] = pFace->v((IndexEdge+2)%3);
				pVertex[3] = pFaceNeighbor->v((IndexNeighbor+2)%3);

				ASSERT(pVertex[0] != NULL &&  
					     pVertex[1] != NULL &&  
							 pVertex[2] != NULL &&  
							 pVertex[3] != NULL);

				// For each composant
				float x = (3.0f * (pVertex[0]->x() + pVertex[1]->x()) + 
					                 pVertex[2]->x() + pVertex[3]->x()) / 8.0f;
				float y = (3.0f * (pVertex[0]->y() + pVertex[1]->y()) + 
					                 pVertex[2]->y() + pVertex[3]->y()) / 8.0f;
				float z = (3.0f * (pVertex[0]->z() + pVertex[1]->z()) + 
					                 pVertex[2]->z() + pVertex[3]->z()) / 8.0f;

				// Add vertex to global mesh array, and set face's vertex
				CVertex3d *pNewVertex = new CVertex3d(x,y,z);
				m_ArrayVertex.Add(pNewVertex);
				pFace->v(3+IndexCurrent,pNewVertex);
			}
		}

		// Set flag
		pFace->SetFlag(1); 
	}

	//*****************************
	// Create faces
	//*****************************
	TRACE("  creating faces (%d faces)\n",NbFace);
	// For each valid face
	for(i=0;i<NbFace;i++)
	{
		CFace3d *pFace = m_ArrayFace[i];
		pFace->SetFlag(0);

		// Valid face
		int NbVertex = pFace->NbVertex();
		CFace3d *pNewFace;

		switch(NbVertex)
		{
		case 4:
			// Create one face

			// On edge 0
			if(pFace->v(3) != NULL)
			{
				pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(2));
				m_ArrayFace.Add(pNewFace);

				// Move current face
				pFace->v(1,pFace->v(3));
			}
			else
				// On edge 1
				if(pFace->v(4) != NULL)
				{
					pNewFace = new CFace3d(pFace->v(0),pFace->v(4),pFace->v(2));
					m_ArrayFace.Add(pNewFace);

					// Move current face
					pFace->v(2,pFace->v(4));
				}
				else
					// On edge 2
					if(pFace->v(5) != NULL)
					{
						pNewFace = new CFace3d(pFace->v(5),pFace->v(1),pFace->v(2));
						m_ArrayFace.Add(pNewFace);

						// Move current face
						pFace->v(2,pFace->v(5));
					}
			break;
		case 5:
			// Create two faces

			// On edge 0 & 2
			if(pFace->v(3) != NULL && 
				 pFace->v(5) != NULL)
			{
				pNewFace = new CFace3d(pFace->v(0),pFace->v(3),pFace->v(5));
				m_ArrayFace.Add(pNewFace);
				pNewFace = new CFace3d(pFace->v(5),pFace->v(3),pFace->v(2));
				m_ArrayFace.Add(pNewFace);

				// Move current face
				pFace->v(0,pFace->v(3));
			}
			else
				// On edge 0 & 1
				if(pFace->v(3) != NULL && 
					 pFace->v(4) != NULL)
				{
					pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(4));
					m_ArrayFace.Add(pNewFace);
					pNewFace = new CFace3d(pFace->v(0),pFace->v(3),pFace->v(4));
					m_ArrayFace.Add(pNewFace);

					// Move current face
					pFace->v(1,pFace->v(4));
				}
				else
					// On edge 1 & 2
					if(pFace->v(4) != NULL && 
						 pFace->v(5) != NULL)
					{
						pNewFace = new CFace3d(pFace->v(1),pFace->v(4),pFace->v(5));
						m_ArrayFace.Add(pNewFace);
						pNewFace = new CFace3d(pFace->v(4),pFace->v(2),pFace->v(5));
						m_ArrayFace.Add(pNewFace);

						// Move current face
						pFace->v(2,pFace->v(5));
					}
			break;
		case 6:
			// Create three faces

			// First (v3,v1,v4)
			pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(4));
			m_ArrayFace.Add(pNewFace);
			// Second (v3,v4,v5)
			pNewFace = new CFace3d(pFace->v(3),pFace->v(4),pFace->v(5));
			m_ArrayFace.Add(pNewFace);
			// Third (v5,v4,v2)
			pNewFace = new CFace3d(pFace->v(5),pFace->v(4),pFace->v(2));
			m_ArrayFace.Add(pNewFace);
			
			// Move current face
			pFace->v(1,pFace->v(3));
			pFace->v(2,pFace->v(5));

			break;
		}
		// Remove subdivision info
		for(int k=3;k<6;k++)
			pFace->v(k,NULL);

	}
	TRACE("  end creating faces (%d faces)\n",m_ArrayFace.GetSize());


	//*****************************
	// Move original vertices
	//*****************************

	// Copy
	TRACE("  copy\n");
	CArray3d<CVertex3d> ArrayVertex;
	ArrayVertex.SetSize(NbVertex);
	for(i=0;i<NbVertex;i++)
		ArrayVertex.SetAt(i,new CVertex3d);

	// For each vertex (at least 2 neighbors)
	for(i=0;i<NbVertex;i++)
	{
		CVertex3d *pVertex = m_ArrayVertex[i];
		int n = pVertex->NbVertexNeighbor();

		// Spline on boundaries, we need two vertices
		// remind that the adjacency has not been 
		// updated yet
		// weighting: 1 6 1
		if(pVertex->GetFlag())
		{
			CVertex3d *pv[2];
			int index = 0;
			for(int k=0;k<n;k++)
			{
				CVertex3d *pNVertex = pVertex->GetVertexNeighbor(k);
				if(pNVertex->GetFlag())
					pv[index++] = pNVertex;
			}
			ASSERT(index == 2);
			ArrayVertex[i]->Set(0.125f*(6.0f*pVertex->x()+pv[0]->x()+pv[1]->x()),
				                  0.125f*(6.0f*pVertex->y()+pv[0]->y()+pv[1]->y()),
													0.125f*(6.0f*pVertex->z()+pv[0]->z()+pv[1]->z()));
		}
		else
		{
			float alpha = Alpha(n);
			float tmp = alpha + (float)n;

			// For each componant
			for(unsigned int j=0;j<3;j++)
			{
				float value = alpha * pVertex->Get(j);
				for(int k=0;k<n;k++)
					value += pVertex->GetVertexNeighbor(k)->Get(j);
				value /= tmp;
				ArrayVertex[i]->Set(j,value);
			}
		}
	}

	// Restore
	TRACE("  restore\n");
	for(i=0;i<NbVertex;i++)
		for(unsigned int j=0;j<3;j++)
			m_ArrayVertex[i]->Set(j,ArrayVertex[i]->Get(j));

	ArrayVertex.Free();

	// Rebuild adjacency and normals
	BuildAdjacency();
	CalculateNormalPerFace();
	CalculateNormalPerVertex();
	m_Modified = 1;

	TRACE("End loop's subdivision (%d faces, %d vertices)\n",
		m_ArrayFace.GetSize(),m_ArrayVertex.GetSize());

	return 1;
}


//********************************************
// Subdivision
// Simple : 1->4
//********************************************
int CMesh3d::Subdivision(void)
{
	// We assume adjacency is built
	int NbVertex = m_ArrayVertex.GetSize();

	// Create subdivision info (edge vertices)
	int NbFace = m_ArrayFace.GetSize();
	TRACE("Start subdivision (%d faces, %d vertices)\n",NbFace,NbVertex);

	TRACE("  subdivision info (%d vertices)\n",m_ArrayVertex.GetSize());

	// For each face
	for(int i=0;i<NbFace;i++)
	{
		CFace3d *pFace = m_ArrayFace[i];

		// On each edge
		for(int IndexEdge=0;IndexEdge<3;IndexEdge++)
		{
			// Get IndexEdge on neighbor
			int IndexCurrent,IndexNeighbor;
			CFace3d *pFaceNeighbor = pFace->f(IndexEdge);

			// No neighbor on this edge, go to next
			if(pFaceNeighbor != NULL)
			{

				VERIFY(pFace->Share2Vertex(pFaceNeighbor,&IndexCurrent,&IndexNeighbor));
				ASSERT(IndexCurrent == IndexEdge);

				// If neighboring face has been treated, then get vertex
				// and go to next step
				if(pFaceNeighbor->GetFlag())
				{
					CVertex3d *pVertex = pFaceNeighbor->v(3+(IndexNeighbor%3));
					pFace->v(3+(IndexEdge%3),pVertex);
					continue;
				}
			}

			// Vertex weighting
			// 0 & 1 : weight : 1
			CVertex3d *pVertex[2];

			// Weight : 1
			pVertex[0] = pFace->v(IndexEdge);
			pVertex[1] = pFace->v((IndexEdge+1)%3);

			ASSERT(pVertex[0] != NULL &&  pVertex[1] != NULL);

			// For each composant
			float coord[3];
			for(int k=0;k<3;k++)
				coord[k] = (pVertex[0]->Get(k) + pVertex[1]->Get(k)) / 2.0f;

			// Add vertex to global mesh array, and set face's vertex
			CVertex3d *pNewVertex = new CVertex3d(coord[0],coord[1],coord[2]);
			m_ArrayVertex.Add(pNewVertex);
			
			pFace->v(3+IndexEdge,pNewVertex);
		}

		// Set flag
		pFace->SetFlag(1); 
	}

	//*****************************
	// Create faces
	//*****************************
	TRACE("  creating faces (%d faces)\n",NbFace);
	// For each valid face
	for(i=0;i<NbFace;i++)
	{
		CFace3d *pFace = m_ArrayFace[i];
		pFace->SetFlag(0);

		// Valid face
		int NbVertex = pFace->NbVertex();
		CFace3d *pNewFace;

		switch(NbVertex)
		{
		case 4:
			// Create one face

			// On edge 0
			if(pFace->v(3) != NULL)
			{
				pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(2));
				m_ArrayFace.Add(pNewFace);

				// Move current face
				pFace->v(1,pFace->v(3));
			}
			else
				// On edge 1
				if(pFace->v(4) != NULL)
				{
					pNewFace = new CFace3d(pFace->v(0),pFace->v(4),pFace->v(2));
					m_ArrayFace.Add(pNewFace);

					// Move current face
					pFace->v(2,pFace->v(4));
				}
				else
					// On edge 2
					if(pFace->v(5) != NULL)
					{
						pNewFace = new CFace3d(pFace->v(5),pFace->v(1),pFace->v(2));
						m_ArrayFace.Add(pNewFace);

						// Move current face
						pFace->v(2,pFace->v(5));
					}
			break;
		case 5:
			// Create two faces

			// On edge 0 & 2
			if(pFace->v(3) != NULL && 
				 pFace->v(5) != NULL)
			{
				pNewFace = new CFace3d(pFace->v(0),pFace->v(3),pFace->v(5));
				m_ArrayFace.Add(pNewFace);
				pNewFace = new CFace3d(pFace->v(5),pFace->v(3),pFace->v(2));
				m_ArrayFace.Add(pNewFace);

				// Move current face
				pFace->v(0,pFace->v(3));
			}
			else
				// On edge 0 & 1
				if(pFace->v(3) != NULL && 
					 pFace->v(4) != NULL)
				{
					pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(4));
					m_ArrayFace.Add(pNewFace);
					pNewFace = new CFace3d(pFace->v(0),pFace->v(3),pFace->v(4));
					m_ArrayFace.Add(pNewFace);

					// Move current face
					pFace->v(1,pFace->v(4));
				}
				else
					// On edge 1 & 2
					if(pFace->v(4) != NULL && 
						 pFace->v(5) != NULL)
					{
						pNewFace = new CFace3d(pFace->v(1),pFace->v(4),pFace->v(5));
						m_ArrayFace.Add(pNewFace);
						pNewFace = new CFace3d(pFace->v(4),pFace->v(2),pFace->v(5));
						m_ArrayFace.Add(pNewFace);

						// Move current face
						pFace->v(2,pFace->v(5));
					}
			break;
		case 6:
			// Create three faces

			// First (v3,v1,v4)
			pNewFace = new CFace3d(pFace->v(3),pFace->v(1),pFace->v(4));
			m_ArrayFace.Add(pNewFace);
			// Second (v3,v4,v5)
			pNewFace = new CFace3d(pFace->v(3),pFace->v(4),pFace->v(5));
			m_ArrayFace.Add(pNewFace);
			// Third (v5,v4,v2)
			pNewFace = new CFace3d(pFace->v(5),pFace->v(4),pFace->v(2));
			m_ArrayFace.Add(pNewFace);
			
			// Move current face
			pFace->v(1,pFace->v(3));
			pFace->v(2,pFace->v(5));

			break;
		}
		// Remove subdivision info
		for(int k=3;k<6;k++)
			pFace->v(k,NULL);

	}
	TRACE("  end creating faces (%d faces)\n",m_ArrayFace.GetSize());

	// Rebuild adjacency and normals
	BuildAdjacency();
	CalculateNormalPerFace();
	CalculateNormalPerVertex();
	m_Modified = 1;

	TRACE("End subdivision (%d faces, %d vertices)\n",
		m_ArrayFace.GetSize(),m_ArrayVertex.GetSize());

	return 1;
}

//********************************************
// Smooth
// 30/09/98
//********************************************
int CMesh3d::Smooth(int MoveOnBundary /* = 1 */)
{
	// We assume adjacency is built
	int NbVertex = m_ArrayVertex.GetSize();

	// Create subdivision info (edge vertices)
	int NbFace = m_ArrayFace.GetSize();
	TRACE("Start smoothing (%d faces, %d vertices)",NbFace,NbVertex);

	//*****************************
	// Move original vertices
	//*****************************

	// Copy
	TRACE(".");
	CArray3d<CVertex3d> ArrayVertex;
	ArrayVertex.SetSize(NbVertex);
	for(int i=0;i<NbVertex;i++)
		ArrayVertex.SetAt(i,new CVertex3d);

	// For each vertex (at least 3 neighbors)
	for(i=0;i<NbVertex;i++)
	{
		CVertex3d *pVertex = m_ArrayVertex[i];

		if(!MoveOnBundary)
			if(pVertex->IsOnBoundary())
			{
				ArrayVertex[i]->Set(pVertex);	
				continue;
			}

		int n = pVertex->NbVertexNeighbor();
		float alpha = Alpha(n);
		float tmp = alpha + (float)n;
		// For each composant
		for(unsigned int j=0;j<3;j++)
		{
			float value = alpha * pVertex->Get(j);
			for(int k=0;k<n;k++)
				value += pVertex->GetVertexNeighbor(k)->Get(j);
			value /= tmp;
			ArrayVertex[i]->Set(j,value);
		}
	}

	// Restore
	TRACE(".");
	for(i=0;i<NbVertex;i++)
		for(unsigned int j=0;j<3;j++)
			m_ArrayVertex[i]->Set(j,ArrayVertex[i]->Get(j));

	// Cleanup
	TRACE(".");
	ArrayVertex.Free();

	TRACE("ok\n");

	// Rebuild adjacency and normals
	BuildAdjacency();
	CalculateNormalPerFace();
	CalculateNormalPerVertex();
	m_Modified = 1;

	return 1;
}






//********************************************
// ColorCurvature
// Each face is colored, function of mean curvature
//********************************************
void CMesh3d::ColorCurvature(CColorRamp *pRamp)
{
	TRACE("Start coloring mesh (curvature)\n");
	int NbVertex = m_ArrayVertex.GetSize();
	TRACE("  %d vertices\n",NbVertex);

	double *pMax = new double[NbVertex];

	// Store curvatures
	for(int i=0;i<NbVertex;i++)
		pMax[i] = m_ArrayVertex[i]->GetMaxAngleAround();

	// Process extremas
	double min = MAX_DOUBLE;
	double max = 0.0f;
	for(i=0;i<NbVertex;i++)
	{
		min = (pMax[i] < min) ? pMax[i] : min;
		max = (pMax[i] > max) ? pMax[i] : max;
	}

	min = (min < 0.0f) ? 0.0f : min;
	double amplitude = max-min;//max-min;
	TRACE("  min : %g\n",min);
	TRACE("  max : %g\n",max);
	TRACE("  amplitude : %g\n",amplitude);
	for(i=0;i<NbVertex;i++)
	{
		unsigned char _grey = (unsigned char)((pMax[i]-min)/amplitude * 255.0f);
		unsigned char grey = _grey > (unsigned char)255 ? (unsigned char)255 : _grey;
		m_ArrayVertex[i]->SetColor(pRamp->Red(grey),pRamp->Green(grey),pRamp->Blue(grey));
	}

	SetModified();

	TRACE("End coloring mesh (curvature)\n");
}

//********************************************
// ColorSpaceNormal
// Each vertex is colored, function of 
// normal space (sum of angles between
// adjacent faces)
//********************************************
void CMesh3d::ColorNormalSpace(CColorRamp *pRamp)
{
	TRACE("Start coloring mesh (space of normals)\n");
	int NbVertex = m_ArrayVertex.GetSize();
	TRACE("  %d vertices\n",NbVertex);
	
	double *pSum = new double[NbVertex];

	// Store curvatures
	for(int i=0;i<NbVertex;i++)
		m_ArrayVertex[i]->NormalMax(&pSum[i]);

	// Process extremas
	double min = MAX_DOUBLE;
	double max = 0.0f;
	for(i=0;i<NbVertex;i++)
	{
		min = (pSum[i] < min) ? pSum[i] : min;
		max = (pSum[i] > max) ? pSum[i] : max;
	}

	min = (min < 0.0f) ? 0.0f : min;
	double amplitude = max-min;//max-min;
	TRACE("  min : %g\n",min);
	TRACE("  max : %g\n",max);
	TRACE("  amplitude : %g\n",amplitude);
	for(i=0;i<NbVertex;i++)
	{
		unsigned char _grey = (unsigned char)((pSum[i]-min)/amplitude * 255.0f);
		unsigned char grey = _grey > (unsigned char)255 ? (unsigned char)255 : _grey;
		//unsigned char grey = (unsigned char)((pCurvature[i]-min)/amplitude * 255.0f);
		m_ArrayVertex[i]->SetColor(pRamp->Red(grey),pRamp->Green(grey),pRamp->Blue(grey));
	}

	SetModified();
	delete [] pSum;

	TRACE("End coloring mesh (space of normals)\n");
}


//********************************************
// ColorCompacity
// Each face is colored, function of face
// compacity
//********************************************
void CMesh3d::ColorCompacity(CColorRamp *pRamp)
{
	TRACE("Start coloring mesh (compacity)\n");
	int NbFace = m_ArrayFace.GetSize();
	TRACE("  %d face(s)\n",NbFace);
	
	double *pCompacity = new double[NbFace];

	// Store compacity
	for(int i=0;i<NbFace;i++)
		pCompacity[i] = m_ArrayFace[i]->Compacity();

	// Process extremas
	double min = MAX_DOUBLE;
	double max = 0.0f;
	for(i=0;i<NbFace;i++)
	{
		min = (pCompacity[i] < min) ? pCompacity[i] : min;
		max = (pCompacity[i] > max) ? pCompacity[i] : max;
	}

	double amplitude = max-min;//max-min;
	TRACE("  min : %g\n",min);
	TRACE("  max : %g\n",max);
	TRACE("  amplitude : %g\n",amplitude);
	for(i=0;i<NbFace;i++)
	{
		unsigned char _grey = (unsigned char)((pCompacity[i]-min)/amplitude * 255.0f);
		unsigned char grey = _grey > (unsigned char)255 ? (unsigned char)255 : _grey;
		//unsigned char grey = (unsigned char)((pCurvature[i]-min)/amplitude * 255.0f);
		m_ArrayFace[i]->SetColor(pRamp->Red(grey),pRamp->Green(grey),pRamp->Blue(grey));
	}

	SetModified();
	delete [] pCompacity;

	TRACE("End coloring mesh (compacity)\n");
}

//********************************************
// ColorHeight
// Each vertex is colored, function of height
//********************************************
void CMesh3d::ColorHeight(CColorRamp *pRamp)
{
	// Color vertices
	int NbVertex = m_ArrayVertex.GetSize();
	double min = MAX_DOUBLE;
	double max = 0.0f;
	for(int i=0;i<NbVertex;i++)
	{
		float height = m_ArrayVertex[i]->y();
		min = (height < min) ? height : min;
		max = (height > max) ? height : max;
	}
	double amplitude = max-min;
	for(i=0;i<NbVertex;i++)
	{
		float height = m_ArrayVertex[i]->y();
		unsigned char _grey = (unsigned char)((height-min)/amplitude * 255.0f);
		unsigned char grey = _grey > (unsigned char)255 ? (unsigned char)255 : _grey;
		m_ArrayVertex[i]->SetColor(pRamp->Red(grey),pRamp->Green(grey),pRamp->Blue(grey));
	}

	// Color faces
	int NbFace = m_ArrayFace.GetSize();
	TRACE("  %d faces\n",NbFace);
	for(i=0;i<NbFace;i++)
	{
		float height = (m_ArrayFace[i]->v1()->y()+
			              m_ArrayFace[i]->v1()->y()+
									  m_ArrayFace[i]->v1()->y())/3.0f;
		unsigned char _grey = (unsigned char)((height-min)/amplitude * 255.0f);
		unsigned char grey = _grey > (unsigned char)255 ? (unsigned char)255 : _grey;
		m_ArrayFace[i]->SetColor(pRamp->Red(grey),pRamp->Green(grey),pRamp->Blue(grey));
	}
	SetModified();
}



//////////////////////////////////////////////
// PREDEFINED
//////////////////////////////////////////////

//********************************************
// GenerateBox
//********************************************
int CMesh3d::GenerateBox(float dx,
												 float dy,
												 float dz)
{
	TRACE("Generate box...");
	CVertex3d *pVertex;

	pVertex = new CVertex3d(-dx/2,-dy/2,-dz/2);
	m_ArrayVertex.Add(pVertex);

	pVertex = new CVertex3d(-dx/2,+dy/2,-dz/2);
	m_ArrayVertex.Add(pVertex);

	pVertex = new CVertex3d(+dx/2,+dy/2,-dz/2);
	m_ArrayVertex.Add(pVertex);

	pVertex = new CVertex3d(+dx/2,-dy/2,-dz/2);
	m_ArrayVertex.Add(pVertex);

	pVertex = new CVertex3d(-dx/2,-dy/2,+dz/2);
	m_ArrayVertex.Add(pVertex);

	pVertex = new CVertex3d(-dx/2,+dy/2,+dz/2);
	m_ArrayVertex.Add(pVertex);

	pVertex = new CVertex3d(+dx/2,+dy/2,+dz/2);
	m_ArrayVertex.Add(pVertex);

	pVertex = new CVertex3d(+dx/2,-dy/2,+dz/2);
	m_ArrayVertex.Add(pVertex);

	CFace3d *pFace;
	pFace = new CFace3d(m_ArrayVertex[0],
		                  m_ArrayVertex[1],
		                  m_ArrayVertex[3]);
	pFace->SetNormal(0.0f,0.0f,-1.0f);
	m_ArrayFace.Add(pFace);

	pFace = new CFace3d(m_ArrayVertex[3],
		                  m_ArrayVertex[1],
		                  m_ArrayVertex[2]);
	pFace->SetNormal(0.0f,0.0f,-1.0f);
	m_ArrayFace.Add(pFace);

	pFace = new CFace3d(m_ArrayVertex[0],
		                  m_ArrayVertex[4],
		                  m_ArrayVertex[1]);
	pFace->SetNormal(-1.0f,0.0f,0.0f);
	m_ArrayFace.Add(pFace);

	pFace = new CFace3d(m_ArrayVertex[1],
		                  m_ArrayVertex[4],
		                  m_ArrayVertex[5]);
	pFace->SetNormal(-1.0f,0.0f,0.0f);
	m_ArrayFace.Add(pFace);

	pFace = new CFace3d(m_ArrayVertex[3],
		                  m_ArrayVertex[2],
		                  m_ArrayVertex[7]);
	m_ArrayFace.Add(pFace);
	pFace->SetNormal(1.0f,0.0f,0.0f);

	pFace = new CFace3d(m_ArrayVertex[7],
		                  m_ArrayVertex[2],
		                  m_ArrayVertex[6]);
	m_ArrayFace.Add(pFace);
	pFace->SetNormal(1.0f,0.0f,0.0f);

	pFace = new CFace3d(m_ArrayVertex[4],
		                  m_ArrayVertex[0],
		                  m_ArrayVertex[3]);
	m_ArrayFace.Add(pFace);
	pFace->SetNormal(0.0f,-1.0f,0.0f);

	pFace = new CFace3d(m_ArrayVertex[7],
		                  m_ArrayVertex[4],
		                  m_ArrayVertex[3]);
	m_ArrayFace.Add(pFace);
	pFace->SetNormal(0.0f,-1.0f,0.0f);

	pFace = new CFace3d(m_ArrayVertex[6],
		                  m_ArrayVertex[4],
		                  m_ArrayVertex[7]);
	m_ArrayFace.Add(pFace);
	pFace->SetNormal(0.0f,0.0f,1.0f);

	pFace = new CFace3d(m_ArrayVertex[6],
		                  m_ArrayVertex[5],
		                  m_ArrayVertex[4]);
	m_ArrayFace.Add(pFace);
	pFace->SetNormal(0.0f,0.0f,1.0f);

	pFace = new CFace3d(m_ArrayVertex[1],
		                  m_ArrayVertex[5],
		                  m_ArrayVertex[6]);
	m_ArrayFace.Add(pFace);
	pFace->SetNormal(0.0f,1.0f,0.0f);

	pFace = new CFace3d(m_ArrayVertex[2],
		                  m_ArrayVertex[1],
		                  m_ArrayVertex[6]);
	m_ArrayFace.Add(pFace);
	pFace->SetNormal(0.0f,1.0f,0.0f);

	TRACE("ok\n");

	return 1;
}

//********************************************
// GenerateMap
//********************************************
int CMesh3d::GenerateMap(int line,
												 int col,
												 float min,
												 float max)
{
	TRACE("Generate map...");
	float x,y,z;
	int i,j;

	// Set vertices
	for(i=0;i<col;i++)
		for(j=0;j<line;j++)
			{
			x = min + ((float)i/(float)line)*(max-min);
			z = min + ((float)j/(float)line)*(max-min);
			y = (float)(cos(x)*cos(z));
			m_ArrayVertex.Add(new CVertex3d(x,y,z));
			}

	// Set faces
	for(i=0;i<col-1;i++)
		for(j=0;j<line-1;j++)
			{
			CVertex3d *pVertex1 = m_ArrayVertex[line*i+j];
			CVertex3d *pVertex2 = m_ArrayVertex[line*i+j+1];
			CVertex3d *pVertex3 = m_ArrayVertex[line*(i+1)+j+1];
			CVertex3d *pVertex4 = m_ArrayVertex[line*(i+1)+j];

			m_ArrayFace.Add(new CFace3d(pVertex1,pVertex2,pVertex3));
			m_ArrayFace.Add(new CFace3d(pVertex1,pVertex3,pVertex4));
			}

	TRACE("ok\n");
	return 1;
}


//********************************************
// GenerateMapFromImage
//********************************************
int CMesh3d::GenerateMap(CTexture *pTexture,
												 int width,
												 int height,
												 int FlagColor /* = 1 */)
{
	// Cleanup
	Free();

	int WidthImage = pTexture->GetWidth();
	int HeightImage = pTexture->GetHeight();
	unsigned char red,green,blue;

	// Vertices
	for(int j=0;j<height;j++)
		for(int i=0;i<width;i++)
		{
			int xImage = (int)((float)i/(float)width*(float)WidthImage);
			int yImage = (int)((float)j/(float)height*(float)HeightImage);
			int index = m_ArrayVertex.Add(new CVertex3d((float)i,(float)pTexture->Grey(xImage,yImage),(float)j));
			if(FlagColor)
			{
				pTexture->Color(xImage,yImage,&red,&green,&blue);
				m_ArrayVertex[index]->SetColor(red,green,blue);
			}
		}

	// Faces
	for(j=0;j<height-1;j++)
		for(int i=0;i<width-1;i++)
		{
			int index = m_ArrayFace.Add(new CFace3d(m_ArrayVertex[j*width+i+1],
				                                      m_ArrayVertex[j*width+i],
																							m_ArrayVertex[(j+1)*width+i+1]));
			m_ArrayFace.Add(new CFace3d(m_ArrayVertex[(j+1)*width+i+1],
				                          m_ArrayVertex[j*width+i],
																	m_ArrayVertex[(j+1)*width+i]));
			if(FlagColor)
			{
				m_ArrayFace[index]->SetColor(*m_ArrayVertex[j*width+i]->GetColor());
				m_ArrayFace[index+1]->SetColor(*m_ArrayVertex[j*width+i+1]->GetColor());
			}
		}

	// Rebuild
	BuildAdjacency();
	CalculateNormalPerFace();
	CalculateNormalPerVertex();

	return 1;
}

/////////////////////////////////////////////
// INTERSECTION
/////////////////////////////////////////////

//********************************************
// NearestIntersectionWithLine
// Non-optimized
// Nearest -> distance from pV0 to pVertexResult
//********************************************
int CMesh3d::NearestIntersectionWithLine(CVertex3d *pV0,
																				 CVertex3d *pV1,
																				 CVertex3d *pVertexResult,
																				 CFace3d **ppFaceResult,
																				 int *pNbFaceVisited)
{
	return ::NearestIntersectionWithLine(&m_ArrayFace,pV0,pV1,pVertexResult,ppFaceResult,pNbFaceVisited);
}

/////////////////////////////////////////////
// I/O
/////////////////////////////////////////////

//********************************************
// WriteFile
//********************************************
int CMesh3d::WriteFile(CStdioFile &file)
{
	CString string;
	TRY
	{
		// Comment
		string.Format("# Mesh : %d vertices, %d faces\n",NbVertex(),NbFace());
		file.WriteString(string);

		// First line
		file.WriteString("DEF Mesh-ROOT Transform {\n");

		// Transform
		string.Format("  translation %g %g %g\n",m_Transform.GetTranslation()->x(),
		                                         m_Transform.GetTranslation()->y(),
																						 m_Transform.GetTranslation()->z());
		file.WriteString(string);
		string.Format("  rotation %g %g %g %g\n",m_Transform.GetRotation()->x(),
                                             m_Transform.GetRotation()->y(),
		                                         m_Transform.GetRotation()->z(),
																					   m_Transform.GetValueRotation()/360.0f*2*3.14159265359f);
		file.WriteString(string);
		string.Format("  scale %g %g %g\n",m_Transform.GetScale()->x(),
			                                 m_Transform.GetScale()->y(),
			                                 m_Transform.GetScale()->z());
		file.WriteString(string);

		// Material
		file.WriteString("  children [\n");
		file.WriteString("    Shape {\n");
		file.WriteString("      appearance Appearance {\n");
		file.WriteString("        material Material {\n");
		file.WriteString("          diffuseColor 0 0 0\n"); // todo
		file.WriteString("        }\n");
		file.WriteString("      }\n");

		// Geometry
		file.WriteString("      geometry DEF Mesh-FACES IndexedFaceSet {\n");
		file.WriteString("        ccw TRUE\n");
		file.WriteString("        solid TRUE\n");

		// Vertices
		file.WriteString("        coord DEF Mesh-COORD Coordinate { point [\n");
		int NbVertex = m_ArrayVertex.GetSize();
		for(int i=0;i<NbVertex;i++)
		{
			string.Format("          %g %g %g",m_ArrayVertex[i]->x(),
		                                     m_ArrayVertex[i]->y(),
																	       m_ArrayVertex[i]->z());
			file.WriteString(string);
			if(i!=(NbVertex-1))
				file.WriteString(",\n");
			else
				file.WriteString("]\n");
		}
		file.WriteString("        }\n");

		// Faces
		file.WriteString("        coordIndex [\n");
		int NbFace = m_ArrayFace.GetSize();
		for(i=0;i<NbFace;i++)
		{
			string.Format("          %d, %d, %d, -1",m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v(0)),
			                                         m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v(1)),
																						   m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v(2)));
			file.WriteString(string);
			if(i!=(NbFace-1))
				file.WriteString(",\n");
			else
				file.WriteString("]\n");
		}

		// End
		file.WriteString("        }\n");
		file.WriteString("      }\n");
		file.WriteString("    ]\n");
		file.WriteString("  }\n\n");

	}
	CATCH(CFileException, e)
	{
		#ifdef _DEBUG
				afxDump << "Error during writing transform" << e->m_cause << "\n";
		#endif
		AfxMessageBox("Error during writing transform");
		return 0;
	}
	END_CATCH

	return 1;
}

//********************************************
// WriteFileRaw (binary raw mode)
//********************************************
int CMesh3d::WriteFileRaw(CFile &file)
{
	// A mesh : 
	//*******************************************
	// Transform  : 10 * float 32 bits
	// NbVertices : UINT 32 bits
	// NbFaces    : UINT 32 bits
	// Vertices   : x y z    : 3 x float 32 bits
	// Faces      : v1 v2 v3 : 3 x UINT 32 bits
	//*******************************************
	// Cost : 40 + 8 + 12*(v+f) bytes

	CString string;
	TRY
	{

		// Transform

		// Translation (xyz)
		float x,y,z;
		x = m_Transform.GetTranslation()->x();
		y = m_Transform.GetTranslation()->y();
		z = m_Transform.GetTranslation()->z();
		file.Write(&x,sizeof(float));
		file.Write(&y,sizeof(float));
		file.Write(&z,sizeof(float));

		// Rotation (xyz)
		x = m_Transform.GetRotation()->x();
		y = m_Transform.GetRotation()->y();
		z = m_Transform.GetRotation()->z();
		float v = m_Transform.GetValueRotation();
		file.Write(&x,sizeof(float));
		file.Write(&y,sizeof(float));
		file.Write(&z,sizeof(float));
		file.Write(&v,sizeof(float));

		// Scale (xyz)
		x = m_Transform.GetScale()->x();
		y = m_Transform.GetScale()->y();
		z = m_Transform.GetScale()->z();
		file.Write(&x,sizeof(float));
		file.Write(&y,sizeof(float));
		file.Write(&z,sizeof(float));

		// Geometry 
		// NbVertices
		// NbFaces
		
		unsigned int NbVertex = m_ArrayVertex.GetSize();
		unsigned int NbFace = m_ArrayFace.GetSize();
		file.Write(&NbVertex,sizeof(unsigned int));
		file.Write(&NbFace,sizeof(unsigned int));

		// Vertices
		for(unsigned int i=0;i<NbVertex;i++)
		{
			x = m_ArrayVertex[i]->x();
			y = m_ArrayVertex[i]->y();
			z = m_ArrayVertex[i]->z();
			file.Write(&x,sizeof(float));
			file.Write(&y,sizeof(float));
			file.Write(&z,sizeof(float));
		}

		// Faces
		unsigned int v1,v2,v3;
		for(i=0;i<NbFace;i++)
		{
			v1 = m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v1());
			v2 = m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v2());
			v3 = m_ArrayVertex.IndexFrom(m_ArrayFace[i]->v3());
			file.Write(&v1,sizeof(unsigned int));
			file.Write(&v2,sizeof(unsigned int));
			file.Write(&v3,sizeof(unsigned int));
		}

	}
	CATCH(CFileException, e)
	{
		#ifdef _DEBUG
				afxDump << "Error during writing " << e->m_cause << "\n";
		#endif
		AfxMessageBox("Error during writing");
		return 0;
	}
	END_CATCH

	return 1;
}


//********************************************
// GetMinArea
//********************************************
double CMesh3d::GetMinArea(CFace3d **ppFace /* NULL */)
{
	double min = MAX_DOUBLE;
	int size = m_ArrayFace.GetSize();
	for(int i=0;i<size;i++)
	{
		double area = m_ArrayFace[i]->Area();
		if(area < min)
		{
			min = area;
			if(ppFace != NULL)
				*ppFace = m_ArrayFace[i];
		}
	}
	return min;
}

//********************************************
// GetMeanArea
//********************************************
double CMesh3d::GetMeanArea()
{
	return ::GetMeanArea(&m_ArrayFace);
}

//*********************************
// GenerateEdgeArray 
//*********************************
int CMesh3d::GenerateEdgeArray(CArray3d<CEdge3d> *pArrayEdge,
															 BOOL FlagOnBoundary,
															 char flag,
															 BOOL *pHasBoundary)
{
	ASSERT(pArrayEdge != NULL);
		
	int NbVertex = m_ArrayVertex.GetSize();

	// Set flags
	SetFlagOnVertices(0);

	TRACE("  begin generate edge array\n");
	TRACE("    %d vertices\n",NbVertex);
	TRACE("    %d faces\n",NbFace());
	int NbEdgePrevious = NbVertex+NbFace();
	TRACE("    %d edges previous\n",NbEdgePrevious);
	pArrayEdge->SetSize(NbEdgePrevious);
	int NbEdge = 0;
	TRACE("  begin...");
	for(int i=0;i<NbVertex;i++)
	{
		CVertex3d *pVertex = m_ArrayVertex[i];
		int NbVertexNeighbor = pVertex->NbVertexNeighbor();
		for(int j=0;j<NbVertexNeighbor;j++)
		{
			CVertex3d *pNeighbor = pVertex->GetVertexNeighbor(j);
			ASSERT(pNeighbor != NULL);
			if(pNeighbor->GetFlag() == 0)
			{
				// Alloc
				CEdge3d *pEdge = new CEdge3d(pVertex,pNeighbor);

				// Set faces (at least one)
				CArray3d<CFace3d> array;
				pVertex->FindFaceAroundContainVertex(pNeighbor,array);
				ASSERT(array.GetSize() >= 1);
				pEdge->f1(array[0]);
				if(array.GetSize() > 1)
					pEdge->f2(array[1]);

				if(NbEdge<NbEdgePrevious)
					pArrayEdge->SetAt(NbEdge,pEdge);
				else
					pArrayEdge->Add(pEdge);
				NbEdge++;

				// Flag (optional)
				if(FlagOnBoundary)
					if(pVertex->IsOnBoundary() && pNeighbor->IsOnBoundary())
					{
						*pHasBoundary = TRUE;
						pEdge->SetFlag(flag);
					}
			}
		}
		pVertex->SetFlag(1); // done

		//if(i%(NbVertex/30)==0) { TRACE("."); }

	}
	TRACE("ok\n");
	pArrayEdge->SetSize(NbEdge);
	TRACE("    %d edges\n",NbEdge);

	return 1;
}


//********************************************
// glDrawProjectLine
// paint the current mesh via projection 
// in line mode
//********************************************
void CMesh3d::glDrawProjectLine(CDC *pDC,
																double *modelMatrix,
																double *projMatrix,
																int *viewport,
																COLORREF ColorLine,
																double ratio,
																int height)
{
	TRACE("Draw projected mesh in metafile-based device context\n");
	TRACE("  line mode\n");
	TRACE("  viewport : (%d;%d;%d;%d)\n",viewport[0],viewport[1],viewport[2],viewport[3]);
	TRACE("  model : %g\t%g\t%g\n",modelMatrix[0],modelMatrix[1],modelMatrix[2]);
	TRACE("          %g\t%g\t%g\n",modelMatrix[3],modelMatrix[4],modelMatrix[5]);
	TRACE("          %g\t%g\t%g\n",modelMatrix[6],modelMatrix[7],modelMatrix[8]);
	TRACE("   proj : %g\t%g\t%g\n",projMatrix[0],projMatrix[1],projMatrix[2]);
	TRACE("          %g\t%g\t%g\n",projMatrix[3],projMatrix[4],projMatrix[5]);
	TRACE("          %g\t%g\t%g\n",projMatrix[6],projMatrix[7],projMatrix[8]);

	// Generate edge array (it saves memory)
	CArray3d<CEdge3d> ArrayEdge;
	GenerateEdgeArray(&ArrayEdge);

	// Select pen
	CPen pen(PS_SOLID,0,ColorLine);
	CPen *pOldPen = pDC->SelectObject(&pen);

	double x1,y1,x2,y2,z;
	for(int i=0;i<ArrayEdge.GetSize();i++)
	{
		CEdge3d *pEdge = ArrayEdge[i];
		ASSERT(pEdge != NULL);

		gluProject((double)pEdge->v1()->x(),
			(double)pEdge->v1()->y(),
			(double)pEdge->v1()->z(),
			modelMatrix,
			projMatrix,
			viewport,&x1,&y1,&z);

		gluProject((double)pEdge->v2()->x(),
			(double)pEdge->v2()->y(),
			(double)pEdge->v2()->z(),
			modelMatrix,
			projMatrix,
			viewport,&x2,&y2,&z);

		// Crop to window
		if(x1 < viewport[0] || y1 < viewport[1] || 
			 x1 > viewport[2] || y1 > viewport[3] ||
			 x2 < viewport[0] || y2 < viewport[1] || 
			 x2 > viewport[2] || y2 > viewport[3])
			continue; // crop to window
		else // draw
		{
			pDC->MoveTo((int)(ratio*x1),(int)(ratio*((float)height-y1)));
			pDC->LineTo((int)(ratio*x2),(int)(ratio*((float)height-y2)));
		}
	}
	// Cleanup
	ArrayEdge.Free();
	pDC->SelectObject(pOldPen);
}


//********************************************
// glDrawProjectFace
//********************************************
void CMesh3d::glDrawProjectFace(CDC *pDC,
																double *modelMatrix,
																double *projMatrix,
																int *viewport,
																COLORREF ColorLine,
																COLORREF ColorFace,
																double ratio,
																int height, // window height
																float RatioNbFace) // default -> 1.0
{
	TRACE("Draw projected mesh in metafile-based device context\n");
	TRACE("  face mode\n");
	TRACE("  viewport : (%d;%d;%d;%d)\n",viewport[0],viewport[1],viewport[2],viewport[3]);
	TRACE("  model : %g\t%g\t%g\n",modelMatrix[0],modelMatrix[1],modelMatrix[2]);
	TRACE("          %g\t%g\t%g\n",modelMatrix[3],modelMatrix[4],modelMatrix[5]);
	TRACE("          %g\t%g\t%g\n",modelMatrix[6],modelMatrix[7],modelMatrix[8]);
	TRACE("   proj : %g\t%g\t%g\n",projMatrix[0],projMatrix[1],projMatrix[2]);
	TRACE("          %g\t%g\t%g\n",projMatrix[3],projMatrix[4],projMatrix[5]);
	TRACE("          %g\t%g\t%g\n",projMatrix[6],projMatrix[7],projMatrix[8]);
	CWmfFace *pArray = new CWmfFace[m_ArrayFace.GetSize()];

	// Many thanks to Gaspard Breton for having 
	// implemented the AVL fast z-sorting part.
	ASSERT(pArray);
	CAVL<CWmfFace,double> avl; 
	CWmfFace bidon;
	avl.Register(&bidon,&bidon.zc,&bidon.avl); // z as key

	int NbFaces = m_ArrayFace.GetSize();
	TRACE("  %d faces\n",NbFaces);
	int NbFacesToProcess = (int)(RatioNbFace*(float)NbFaces);
	TRACE("  %d faces to process\n",NbFacesToProcess);
	TRACE("  begin sort...");
	int NbFaceValid = 0;
	for(int i=0;i<NbFaces;i++)
	{
		CFace3d *pFace = m_ArrayFace[i];

		// Compute barycenter as z-reference
	  // Sorting by a triangle average depth does not allow
		// to disambiguate some cases.  Handling these cases would 
		// require breaking up the primitives. Please mail any
		// improvement about this
		double xc = (pFace->v1()->x()+pFace->v2()->x()+pFace->v3()->x())/3;
		double yc = (pFace->v1()->y()+pFace->v2()->y()+pFace->v3()->y())/3;
		double zc = (pFace->v1()->z()+pFace->v2()->z()+pFace->v3()->z())/3;

		// Project center
		gluProject(xc,yc,zc,
			modelMatrix,
			projMatrix,
			viewport,&pArray[i].xc,&pArray[i].yc,&pArray[i].zc);

		// Project three vertices
		gluProject((double)pFace->v1()->x(),
			(double)pFace->v1()->y(),
			(double)pFace->v1()->z(),
			modelMatrix,
			projMatrix,
			viewport,&pArray[i].x1,&pArray[i].y1,&pArray[i].z1);

		gluProject((double)pFace->v2()->x(),
			(double)pFace->v2()->y(),
			(double)pFace->v2()->z(),
			modelMatrix,
			projMatrix,
			viewport,&pArray[i].x2,&pArray[i].y2,&pArray[i].z2);

		gluProject((double)pFace->v3()->x(),
			(double)pFace->v3()->y(),
			(double)pFace->v3()->z(),
			modelMatrix,
			projMatrix,
			viewport,&pArray[i].x3,&pArray[i].y3,&pArray[i].z3);

		// Crop & sort
		if(pArray[i].x1 < viewport[0]  || pArray[i].y1 < viewport[1]  || 
		   pArray[i].x1 > viewport[2]  || pArray[i].y1 > viewport[3]  ||
		   pArray[i].x2 < viewport[0]  || pArray[i].y2 < viewport[1]  || 
		   pArray[i].x2 > viewport[2]  || pArray[i].y2 > viewport[3]  ||
		   pArray[i].x3 < viewport[0]  || pArray[i].y3 < viewport[1]  || 
		   pArray[i].x3 > viewport[2]  || pArray[i].y3 > viewport[3])
		  continue;
		else
		{
			pArray[i].m_Draw = 1;  // yes, insert this triangle
			pArray[i].zc *= -1.0f; // back to front
			avl.Insert(pArray,i);  // insert via sort
			NbFaceValid++;
		}
	}
	TRACE("ok\n");

	// Draw
	CPen pen(PS_SOLID,0,ColorLine);
	CBrush BrushFace(ColorFace);
	CPen *pOldPen = pDC->SelectObject(&pen);
	POINT points[3]; // triangular faces only

	// Default
	CBrush *pOldBrush = pDC->SelectObject(&BrushFace);
	TRACE("begin draw...");
	int nb = 0;
	for(i=avl.GetFirst(pArray);
	    (AVLNULL != i) && nb < NbFacesToProcess;
			i=avl.GetNext(pArray),nb++)
	{
		// Fill and outline the face
		points[0].x = (int)(ratio*pArray[i].x1);
		points[0].y = (int)(ratio*((float)height-pArray[i].y1));
		points[1].x = (int)(ratio*pArray[i].x2);
		points[1].y = (int)(ratio*((float)height-pArray[i].y2));
		points[2].x = (int)(ratio*pArray[i].x3);
		points[2].y = (int)(ratio*((float)height-pArray[i].y3));

		// Fill triangle
		pDC->Polygon(points,3);

		// Outline triangle
		pDC->MoveTo(points[0]);
		pDC->LineTo(points[1]);
		pDC->LineTo(points[2]);
		pDC->LineTo(points[0]);
	}
	TRACE("ok\n");

	// Restore and cleanup
	pDC->SelectObject(pOldPen);
	pDC->SelectObject(pOldBrush);
	delete [] pArray;
}

// ** EOF **